import multiprocessing  # pipinstall
from tkinter.ttk import Combobox
from zipfile import ZipFile, is_zipfile
from unrar.rarfile import RarFile, is_rarfile
from py7zr import SevenZipFile, is_7zfile
from threading import Thread, Lock
from tkinter import Tk, Button, Frame, messagebox, StringVar, Label, Canvas, RIGHT, LEFT
from tkinter.filedialog import askopenfilename, askdirectory, Checkbutton
from os import rename
from os.path import join
from time import sleep
from PsdIterator import PsdIterator

# 形成exe文件：以管理员身份打开终端，再python -w Main.py

__author__ = '广大菜鸟'

succeedMark = False
FINAL_SECRET = ''  # 最终确定的答案
multiprocessing.freeze_support()
lock = Lock()  # 将锁内的代码串行化


def extractZipFile(zipFile, psd, dst):  # 提取文件的类
    print('child_thread (pwd:%s) is running' % psd)
    global succeedMark, FINAL_SECRET
    try:
        zipFile.extractall(dst, pwd=psd.encode('utf-8'))
        for names in zipFile.namelist():
            try:
                new_names = names.encode('cp437').decode('gbk')
            except:
                new_names = names.encode('utf-8').decode('utf-8')
            rename(join(dst, names), join(dst, new_names))
        print('This file\'s password is ', psd)
        lock.acquire()  # 上锁，第一个线程如果申请到锁，会在执行公共数据的过程中持续阻塞后续线程
        # 即后续第二个或其他线程依次来了发现已经被上锁，只能等待第一个线程释放锁
        # 当第一个线程将锁释放，后续的线程会进行争抢
        FINAL_SECRET = psd
        succeedMark = True
        lock.release()  # 释放锁
    except Exception as e:
        print(str(e))


def extractRarFile(rarFile, psd, dst):  # 提取文件的类
    print('child_thread (pwd:%s) is running' % psd)
    global succeedMark, FINAL_SECRET
    try:
        rarFile.extractall(dst, pwd=psd)
        print('This file\'s password is ', psd)
        lock.acquire()
        succeedMark = True
        FINAL_SECRET = psd
        lock.release()
    except Exception as e:
        print(str(e))


def extract7ZFile(psd, src, dst):  # 提取文件的类
    print('child_thread (pwd:%s) is running' % psd)
    global succeedMark, FINAL_SECRET
    try:
        with SevenZipFile(src, password=psd, mode="r") as FileExample:
            FileExample.extractall(dst)
            print('This file\'s password is ', psd)
            lock.acquire()
            FINAL_SECRET = psd
            succeedMark = True
            lock.acquire()
    except Exception as e:
        print(str(e))


class faceClass:
    filenameforReading = ''
    pathforSaving = '.'
    min_num = 1
    max_num = 1
    all_schedule = 0
    current_schedule = 0  # 当前遍历个数

    def __init__(self):
        self.window = Tk()
        self.window.title('暴力解压压缩包')
        self.window.maxsize(400, 200)
        self.window.minsize(400, 200)
        frame1 = Frame(self.window)
        frame1.pack()

        self.digitVar = StringVar()
        self.digitVar.set("F")

        self.characterVar = StringVar()
        self.characterVar.set("F")
        Label(frame1, text="选择密码元素(必选)").grid(row=2, column=2)

        ck1 = Checkbutton(frame1, text='选择数字',
                          variable=self.digitVar,
                          onvalue="T", offvalue="F").grid(row=2, column=3)
        ck2 = Checkbutton(frame1, text='选择字母',
                          variable=self.characterVar,
                          onvalue="T", offvalue="F").grid(row=3, column=3)

        Label(frame1, text="选择密码最少长度").grid(row=4, column=1)

        self.minNumvalueVar = StringVar()  # 窗体自带的文本，新建一个值
        self.comboxlist1 = Combobox(frame1, textvariable=self.minNumvalueVar, state="readonly")  # 初始化
        self.comboxlist1["values"] = tuple([str(i) for i in range(1, 21)])
        self.comboxlist1.current(0)  # 选择第一个
        self.comboxlist1.grid(row=4, column=2)
        self.comboxlist1.bind("<<ComboboxSelected>>", self.modifyMaxMin)

        Label(frame1, text="选择密码最多长度").grid(row=5, column=1)

        self.maxNumvalueVar = StringVar()  # 窗体自带的文本，新建一个值
        self.comboxlist2 = Combobox(frame1, textvariable=self.maxNumvalueVar, state="readonly")  # 初始化
        self.comboxlist2["values"] = tuple([str(i) for i in range(1, 21)])
        self.comboxlist2.current(0)  # 选择第一个
        self.comboxlist2.grid(row=5, column=2)
        self.comboxlist2.bind("<<ComboboxSelected>>", self.modifyMaxMin)

        Button(frame1, text="选择解压文件", command=self.selectFile).grid(row=6, column=1, padx=5)
        Button(frame1, text="选择解压到的文件夹(默认原来目录下)", command=self.selectFolder).grid(row=6, column=2, padx=5)
        Button(frame1, text="解压", command=self.unpack).grid(row=6, column=3, padx=5, pady=5)

        # 内置进度条
        # self.progressbarOne = Progressbar(frame1, length=200, mode='indeterminate', orient=HORIZONTAL)
        # self.progressbarOne.grid(row=7, column=2, padx=5)

        # 进度条面板
        self.progresslabelVar = StringVar()
        self.progresslabelVar.set("当前进度:  0.00%")
        self.progressLabel = Label(self.window, textvariable=self.progresslabelVar).pack(side=LEFT, padx=10, pady=5)
        self.canvas = Canvas(self.window, width=280, height=25, bg='white')
        self.canvas.pack(side=RIGHT, padx=15, pady=5)
        self.fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="green")  # x1,y1,x2,y2.

        self.window.mainloop()

    def modifyMaxMin(self, _):
        self.min_num = int(self.minNumvalueVar.get())
        self.max_num = int(self.maxNumvalueVar.get())
        if self.min_num > self.max_num:  # 预防故意搞事情
            self.min_num, self.max_num = self.max_num, self.min_num
            tmplist1 = list(self.comboxlist1["values"])
            self.comboxlist1.current(tmplist1.index(str(self.min_num)))
            tmplist2 = list(self.comboxlist2["values"])
            self.comboxlist2.current(tmplist2.index(str(self.max_num)))

    def selectFile(self):
        self.filenameforReading = askopenfilename()

    def selectFolder(self):
        self.pathforSaving = askdirectory()

    def unpack(self):
        global FINAL_SECRET
        FINAL_SECRET = ''
        digit = (self.digitVar.get() == 'T')
        character = (self.characterVar.get() == 'T')
        if self.filenameforReading == '':
            messagebox.showwarning("showWarning", "你要先选择文件啊！")
            return
        elif not digit and not character:
            messagebox.showwarning("showWarning", "至少选择一种元素啊！")
            return
        elif self.pathforSaving != '.':
            self.mainStep(self.filenameforReading, dst_folder=self.pathforSaving,
                          min_num=self.min_num, max_num=self.max_num,
                          digit=digit, character=character)
        else:
            self.mainStep(self.filenameforReading, dst_folder=None, min_num=self.min_num, max_num=self.max_num,
                          digit=digit, character=character)
        if FINAL_SECRET != '':
            messagebox.showinfo('Succeed!', '密码是' + FINAL_SECRET)
        else:
            messagebox.showwarning("showWarning", "修改相关参数再试试")

    def mainStep(self, src, dst_folder=None, min_num=1, max_num=1, digit=True, character=True):
        global succeedMark
        self.current_schedule = 0
        succeedMark = False
        if dst_folder is None:
            dst_folder = src[:src.rindex('.')]

        print('parent_thread_running...')
        obj = PsdIterator(min_num, max_num, digit, character)
        myiter = iter(obj)
        if self.min_num > 1:
            self.all_schedule = (len(obj.letters) ** self.max_num - len(obj.letters) ** (self.min_num - 1))
        else:
            self.all_schedule = len(obj.letters) ** self.max_num
        global functionHandle
        functionHandle = extractZipFile  # 默认.zip

        if src.endswith('.zip'):
            functionHandle = extractZipFile
            if not is_zipfile(src):
                raise Exception('This is not a true zip file!')
            with ZipFile(src, mode="r") as FileExample:  # 解压是 r , 压缩是 w 追加压缩是 a
                for psd in myiter:
                    self.current_schedule += 1
                    self.progress()
                    if not succeedMark:
                        t = Thread(target=functionHandle, args=(FileExample, psd, dst_folder))
                        print("current psd=", psd)
                        t.start()
                        t.join()
                    else:
                        break

        elif src.endswith('.rar'):
            functionHandle = extractRarFile
            if not is_rarfile(src):
                raise Exception('This is not a true rar file!')
            with RarFile(src, mode="r") as FileExample:  # 解压是 r , 压缩是 w 追加压缩是 a
                for psd in myiter:
                    self.current_schedule += 1
                    self.progress()
                    if not succeedMark:
                        t = Thread(target=functionHandle, args=(FileExample, psd, dst_folder))
                        t.start()
                        t.join()
                    else:
                        break

        elif src.endswith('.7z'):
            functionHandle = extract7ZFile
            if not is_7zfile(src):
                raise Exception('This is not a true 7z file!')
            for psd in myiter:
                self.current_schedule += 1
                self.progress()
                if not succeedMark:
                    t = Thread(target=functionHandle, args=(psd, src, dst_folder))
                    t.start()
                    t.join()
                else:
                    break

        else:
            print('Sorry!Now we couldn\'t copy with this type of this file.')
        print('parent_thread_exit...')

    def progress(self):
        # if self.current_schedule == 1:
        #     self.progressbarOne.start()
        #     self.window.update()
        # elif self.all_schedule > self.current_schedule:
        #     self.progressbarOne.step(1 / self.all_schedule*200)
        #     self.window.update()
        # else:
        #     self.progressbarOne.stop()
        #     self.window.update()
        self.canvas.coords(self.fill_line, (5, 5, 5 + (self.current_schedule / self.all_schedule) * 240, 25))
        print("当前进度:%6.2f%%" % ((self.current_schedule / self.all_schedule) * 100))
        self.progresslabelVar.set("当前进度:%6.2f%%" % ((self.current_schedule / self.all_schedule) * 100))
        self.window.update()
        sleep(0.5)


if __name__ == '__main__':
    faceClass()
